package com.huawei.cloudapp.model.direct;

import static com.huawei.cloudapp.utils.CasConstantsUtil.X_AUTH_TOKEN;
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
import static java.net.HttpURLConnection.HTTP_OK;
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;

import android.util.Log;

import androidx.annotation.NonNull;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.huawei.cloudapp.model.IConnectInfoModel;
import com.huawei.cloudapp.model.OnRequestListener;
import com.huawei.cloudapp.model.bean.CustomException;
import com.huawei.cloudapp.model.bean.User;
import com.huawei.cloudapp.model.bean.direct.ConnectInfos;
import com.huawei.cloudapp.model.bean.direct.CPHError;
import com.huawei.cloudapp.model.bean.management.CasConnectInfo;
import com.huawei.cloudapp.utils.CasHttpUtils;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;

public class ConnectInfoModel implements IConnectInfoModel {
    private static final String TAG = "ConnectInfoModel";
    private String mRequestBodyJson;
    @Override
    public void getConnectInfo(User user, String phoneId, String region, OnRequestListener<CasConnectInfo> onRequestListener) {
        String url = "https://cph." + region + ".myhuaweicloud.com/v1/"+
                user.getUserProjectId().get(region) + "/cloud-phone/phones/batch-connection";
        String userTokenExpireTime = user.getUserTokenExpireTimeByRegion(region);
        if (userTokenExpireTime == null || userTokenExpireTime.isEmpty()
                || Long.parseLong(userTokenExpireTime) < System.currentTimeMillis()) {
            onRequestListener.onFailure(new CustomException.TokenExpireException());
            return;
        }
        Log.d(TAG, "url: " + url);
        HashMap<String, String> requestHeader = new HashMap<String, String>();
        requestHeader.put(X_AUTH_TOKEN, user.getUserTokenByRegion(region));
        HashMap<String, Object> body = new HashMap<>();
        body.put("phone_ids", Collections.singletonList(phoneId));
        body.put("client_type", "ANDROID");
        Gson gson = new Gson();
        mRequestBodyJson = gson.toJson(body);
        Log.d(TAG, "getConnectInfo: " + mRequestBodyJson);
        CasHttpUtils.post(url, requestHeader, mRequestBodyJson, new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                onRequestListener.onFailure(e);
            }

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                try {
                    onRequestListener.onSuccess(parseResponseToConnectInfo(response), 1);
                } catch (Exception e) {
                    onRequestListener.onFailure(e);
                }
            }
        });
    }

    @Override
    public List<CasConnectInfo> parseResponseToConnectInfo(Response response) throws Exception {
        if (response == null) {
            Log.e(TAG, "Failed to get response from server.");
            throw new CustomException.EmptyResponseFromServerException();
        }
        String rspJsonStr = response.body().string();
        Gson gson = new Gson();
        if (response.code() == HTTP_OK) {
            if (rspJsonStr.isEmpty()){
                Log.e(TAG, "Failed to get start param response.");
                throw new CustomException.EmptyResponseFromServerException();
            }

            ConnectInfos rsp;
            try {
                rsp = gson.fromJson(rspJsonStr, ConnectInfos.class);
            } catch (JsonSyntaxException e) {
                Log.e(TAG, "handleGetConnectInfoResponse: Failed to parse rsp json. " + rspJsonStr);
                throw new CustomException.FailedToGetConnectInfoException();
            }
            if (rsp == null) {
                Log.e(TAG, "handleGetConnectInfoResponse: Failed to get rsp from json. " + response.code() + ", " + rspJsonStr);
                throw new CustomException.FailedToGetConnectInfoException();
            }
            Log.d(TAG, "rsp: " + rspJsonStr);
            //当前接口只会请求一个手机的信息，所以只需要判断一次。
            if (!rsp.getErrors().isEmpty()) {
                Log.e(TAG, "Failed to get connect info, : " + rsp.getErrors().get(0).getErrorMsg());
                throw new CustomException.FailedToGetConnectInfoException(rsp.getErrors().get(0).getErrorMsg());
            }
            ConnectInfos.ConnectInfo ConnectInfo = rsp.getConnectInfos().get(0);
            if (ConnectInfo.getUdid() != null) {
                return Collections.singletonList(
                        GenerateConnectInfo(ConnectInfo.getAccessInfo().getAccessIp(),
                                String.valueOf(ConnectInfo.getAccessInfo().getAccessPort()),
                                ConnectInfo.getAccessInfo().getSessionId(),
                                ConnectInfo.getAccessInfo().getTicket(),
                                ConnectInfo.getAccessInfo().getSessionId(),
                                ConnectInfo.getAccessInfo().getTimestamp()));
            } else {
                return Collections.singletonList(
                        GenerateConnectInfo(ConnectInfo.getAccessInfo().getAccessIp(),
                                String.valueOf(ConnectInfo.getAccessInfo().getAccessPort()),
                                ConnectInfo.getAccessInfo().getSessionId(),
                                ConnectInfo.getAccessInfo().getTicket(),
                                ConnectInfo.getPhoneId(),
                                ConnectInfo.getAccessInfo().getTimestamp()));
            }
        } else {
            CPHError rsp;
            try {
                rsp = gson.fromJson(rspJsonStr, CPHError.class);
            } catch (JsonSyntaxException e) {
                Log.e(TAG, "Failed to parse rsp json. " + rspJsonStr);
                throw new CustomException.FailedToGetPhoneListException();
            }

            if (response.code() == HTTP_BAD_REQUEST || response.code() == HTTP_INTERNAL_ERROR) {
                if (rsp.getErrorCode().equals("CPS.0004")) {
                    Log.e(TAG, "Phone id is invalid, request body :" + mRequestBodyJson + ", rsp: " + rspJsonStr);
                    throw new CustomException.ParamInvalidException();
                } else if (rsp.getErrorCode().equals("CPS.0358")) {
                    Log.e(TAG, "client_type is invalid, request body :" + mRequestBodyJson + ", rsp: " + rspJsonStr);
                    throw new CustomException.ParamInvalidException();
                }  else if (rsp.getErrorCode().equals("CPS.0359")) {
                    Log.e(TAG, "Phone id size is over max limit, request body :" + mRequestBodyJson + ", rsp: " + rspJsonStr);
                    throw new CustomException.ParamInvalidException();
                } else {
                    Log.e(TAG, "handleGetConnectInfoResponse: " + response.code() + ", " + rspJsonStr);
                    throw new CustomException.FailedToGetConnectInfoException(rspJsonStr);
                }
            }  else if (response.code() == HTTP_UNAUTHORIZED) {
                if (rsp.getErrorCode().equals("APIGW.0301") || rsp.getErrorCode().equals("APIGW.0307")) {
                    Log.e(TAG, "Token is invalid, need get new token. ");
                    throw new CustomException.TokenExpireException();
                }  else if (rsp.getErrorCode().equals("APIGW.0303") || rsp.getErrorCode().equals("APIGW.0305")) {
                    Log.e(TAG, "Check whether the account has the permission to request APIs.");
                    throw new CustomException.NoPermissionException();
                } else {
                    Log.e(TAG, "Failed to get connect info, " + response.code() + ", " + rspJsonStr);
                    throw new CustomException.FailedToGetConnectInfoException(rspJsonStr);
                }
            } else if (response.code() == HTTP_FORBIDDEN) {
                if (rsp.getErrorCode().equals("APIGW.0302") || rsp.getErrorCode().equals("APIGW.0304") || rsp.getErrorCode().equals("APIGW.0306")) {
                    Log.e(TAG, "Check whether the account has the permission to request APIs.");
                    throw new CustomException.NoPermissionException();
                } else {
                    Log.e(TAG, "Failed to get connect info, : " + response.code() + ", " + rspJsonStr);
                    throw new CustomException.FailedToGetConnectInfoException(rspJsonStr);
                }
            } else {
                Log.e(TAG, "Failed to get connect info, : " + response.code() + ", " + rspJsonStr);
                throw new CustomException.FailedToGetConnectInfoException(rspJsonStr);
            }
        }
    }

    public CasConnectInfo GenerateConnectInfo(String ip,
                                              String port,
                                              String sessionId,
                                              String ticket,
                                              String aesKey,
                                              String timestamp) throws Exception {
        if (ip.isEmpty() || port.isEmpty()) {
            throw new CustomException.ConnectInfoErrorException();
        }
        if (sessionId.isEmpty()) {
            sessionId = UUID.randomUUID().toString().replaceAll("-", "");
        }
        CasConnectInfo connectorInfo = new CasConnectInfo();
        connectorInfo.setConnectIp(ip);
        connectorInfo.setConnectPort(port);
        connectorInfo.setBackgroundTimeout("60");
        connectorInfo.setAvailablePlayTime("0");
        connectorInfo.setSessionId(sessionId);
        connectorInfo.setTicket(ticket);
        connectorInfo.setAesKey(aesKey);
        connectorInfo.setAuthTs(timestamp);
        connectorInfo.setTouchTimeout("0");

        return connectorInfo;
    }
}
